#ifndef SHARED_VALUE_HH
#define SHARED_VALUE_HH

#include <pthread.h>

namespace talos
{

template< typename __type__ >
class IOPort
{
		class ShValFunc
		{
			public:
				ShValFunc( void ( *fun_ )( void*, void* ),
						void* instance_, void* arg_,
						bool threaded_ = false ) :
				fun( fun_ ), instance( instance_ ),
				arg( arg_ ), threaded( threaded_ ) {}

				static void* starter( void* functor )
				{
					ShValFunc& funct = *( ( ShValFunc * ) functor );
					( *( funct.fun ) )( funct.instance, funct.arg );
					return NULL;
				}

				void launch()
				{
					if ( threaded )
					{
						pthread_create( &thr, NULL, starter, this );
						pthread_detach( thr );
					}
					else
					{
						( *fun )( instance, arg );
					}
				}

				void  ( *fun )( void*, void* );
				void* instance;
				void* arg;
				bool  threaded;
				pthread_t thr;
		};

		typedef ShValCema
		{
			__type__ value;
			pthread_mutex_t lock;
		} ShValCema;

	public:

		/*!
		 * \brief Default Constructor
		 */
		IOPort() :
			m_callback( NULL ),
			m_value( new __type__ ),
			m_sharing_counter( new int ),
			m_head( new (IOPort< __type__ >*) ),
			m_previous( NULL ),
			m_next( NULL )
		{
			*m_sharing_counter = 1;
			*m_head = this;
		}

		/*!
		 * \brief Synchronized copy constructor
		 * \param aIOPort the value to copy and synchronize with.
		 */
		IOPort( IOPort< __type__ >& aIOPort ) :
			m_callback( NULL ),
			m_value( aIOPort.m_value ),
			m_sharing_counter( aIOPort.m_sharing_counter ),
			m_head( aIOPort.m_head ),
			m_previous( &aIOPort ),
			m_next( NULL )
		{
			++( *m_sharing_counter );
			m_previous->m_next = this;
		}

		/*!
		 * \brief Destructor
		 */
		~IOPort()
		{
			if ( *m_sharing_counter > 1 )
			{
				--( *m_sharing_counter );
				if ( *m_head == this )
				{
					*m_head = m_next;
					m_next->m_previous = NULL;
				}
			}
			else
			{
				delete m_sharing_counter;
				delete m_value;
				delete m_head;
			}

			if ( m_callback )
				delete m_callback;
		}

		/*!
		 * \brief Synchronization operator
		 * \param aIOPort The value to synchronize with
		 */
		IOPort< __type__ >& operator=( IOPort< __type__ >& aIOPort )
		{
			if ( *m_sharing_counter > 1 )
			{
				--( *m_sharing_counter );
				if ( *m_head == this )
				{
					*m_head = m_next;
					m_next->m_previous = NULL;
				}
			}
			else
			{
				delete m_sharing_counter;
				delete m_value;
				delete m_head;
			}

			if ( aIOPort.m_next )
			{
				aIOPort.m_next->m_previous = this;
				m_next = aIOPort.m_next->m_previous;
			}
			else
			{
				m_next = NULL;
			}

			aIOPort.m_next = this;
			m_previous = &aIOPort;

			m_head = aIOPort.m_head;
			m_sharing_counter = aIOPort.m_sharing_counter;
			++(*m_sharing_counter);
			m_value = aIOPort.m_value;

			if (m_callback)
			{
				m_callback->arg = m_value;
			}

			return *this;
		}

		/*!
		 * \brief Synchronization operator
		 * \param aIOPort The value to synchronize with
		 */
		IOPort< __type__ >& sync( IOPort< __type__ >& aIOPort )
		{
			if ( *m_sharing_counter > 1 )
			{
				--( *m_sharing_counter );
				if ( *m_head == this )
				{
					*m_head = m_next;
					m_next->m_previous = NULL;
				}
			}
			else
			{
				delete m_sharing_counter;
				delete m_value;
				delete m_head;
			}

			if ( aIOPort.m_next )
			{
				aIOPort.m_next->m_previous = this;
				m_next = aIOPort.m_next->m_previous;
			}
			else
			{
				m_next = NULL;
			}

			aIOPort.m_next = this;
			m_previous = &aIOPort;

			m_head = aIOPort.m_head;
			m_sharing_counter = aIOPort.m_sharing_counter;
			++(*m_sharing_counter);
			m_value = aIOPort.m_value;

			if (m_callback)
			{
				m_callback->arg = m_value;
			}

			return *this;
		}

		IOPort< __type__ >& operator<<( __type__ aValue )
		{
			*m_value = aValue;

			IOPort< __type__ >* ptr = *m_head;

			while ( ptr )
			{
				if ( ptr->m_callback )
				{
					ptr->m_callback->launch();
				}

				ptr = ptr->m_next;
			}

			return *this;
		}

		IOPort< __type__ >& push( __type__ aValue )
		{
			*m_value = aValue;

			IOPort< __type__ >* ptr = *m_head;

			while ( ptr )
			{
				if ( ptr->m_callback )
				{
					ptr->m_callback->launch();
				}

				ptr = ptr->m_next;
			}

			return *this;
		}

		/*!
		 * \brief Get the value of the object as a const reference.
		 */
		const __type__& operator*()
		{
			return *m_value;
		}

		/*!
		 * \brief This function is called by the NotifyChange macro. Don't use it !
		 */
		void setCallBack( void ( *callback )( void*, void* ), void* instance )
		{
			if ( m_callback )
			{
				delete m_callback;
			}

			m_callback = new ShValFunc( callback, instance, m_value );
		}

	private:
		ShValFunc* m_callback;                  /// Callback Functor
		__type__*  m_value;                     /// Stored Value
		int*       m_sharing_counter;           /// Number of synchronized instances

		IOPort< __type__ >** m_head;       /// The first IOPort of the synchronization list
		IOPort< __type__ >*  m_previous;   /// The previous instance in the synchronization list
		IOPort< __type__ >*  m_next;       /// The next instance in the synchronization list
};

}

/*!
 * \brief Macro used to declare a notifier
 * Use it as a function declaration. If you use it to define the function at the same time
 * you have to use the keyword "inputPort" to call the argument.
 */
#define DeclareNotifier( __class__, function, __argtype__ ) \
	/*{*/ \
static void function##__##__class__##__notifier__ ( void* instance, void* arg ) \
{ \
	__class__* inst = ( __class__* ) instance; \
	__argtype__& arg_ = *( ( __argtype__* ) arg );\
	inst->function( arg_ ); \
}
/*}*/

/*!
 * \brief Used to set the function to call when a changement happen.
 */
#define NotifyChange( __class__, function, sharedvalue ) \
	sharedvalue.setCallBack( function##__##__class__##__notifier__, this )


#endif // SHARED_VALUE_HH
